package com.kpmg.datalake.common.service.impl;

import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.ast.statement.SQLAlterStatement;
import com.alibaba.druid.sql.ast.statement.SQLCallStatement;
import com.alibaba.druid.sql.ast.statement.SQLCreateStatement;
import com.alibaba.druid.sql.ast.statement.SQLDeleteStatement;
import com.alibaba.druid.sql.ast.statement.SQLDropStatement;
import com.alibaba.druid.sql.ast.statement.SQLDropUserStatement;
import com.alibaba.druid.sql.ast.statement.SQLExprStatement;
import com.alibaba.druid.sql.ast.statement.SQLInsertStatement;
import com.alibaba.druid.sql.ast.statement.SQLSelectStatement;
import com.alibaba.druid.sql.dialect.oracle.visitor.OracleOutputVisitor;
import com.alibaba.druid.sql.parser.Token;
import com.kpmg.datalake.common.dynamicdatasource.DBTypeEnum;
import com.kpmg.datalake.common.enums.ResultCodeEnum;
import com.kpmg.datalake.common.exception.BusinessException;
import com.kpmg.datalake.common.service.CommonExecuteService;
import com.kpmg.datalake.common.service.SqlParseService;
import com.kpmg.datalake.common.vo.KeyAndValueDTO;
import com.kpmg.datalake.common.vo.ThreeParameter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;

/**
 * @author Alex.zhao [alex.zhao@kpmg.com]
 * @since Created on azhao6 on 2018-08-22 17:50:07
 */
@Service
public class OracleSqlParseServiceImpl implements SqlParseService {

  /**
   * 解析string 变成sql
   *
   * @param sqlData 数据对象
   * @return sql对象
   */
  @Override
  public ThreeParameter<Boolean, List<KeyAndValueDTO<Token, String>>, String> parseStringToSql(
      String sqlData) {
    ThreeParameter<Boolean, List<KeyAndValueDTO<Token, String>>, String> threeParameter = new ThreeParameter<>(
        true, null, null);
    try {
      List<KeyAndValueDTO<Token, String>> arrayList = new ArrayList<>();
      List<SQLStatement> sqlList = SQLUtils.parseStatements(sqlData, DBTypeEnum.ORACLE.getDbType());
      for (SQLStatement sqlStatement : sqlList) {
        KeyAndValueDTO<Token, String> result = new KeyAndValueDTO<>();
        //获取sql 执行输出器
        StringBuilder out = new StringBuilder();
        OracleOutputVisitor oracleOutputVisitor = new OracleOutputVisitor(out);
        sqlStatement.accept(oracleOutputVisitor);
        //获取sql执行器
        result.setKey(this.getOperation(sqlStatement, out.toString()));
        result.setValue(out.toString());
        arrayList.add(result);
      }
      threeParameter.setValue(arrayList);
    } catch (Exception e) {
      threeParameter.setKey(false);
      threeParameter.setMsg(e.getLocalizedMessage());
    }
    return threeParameter;
  }

  @Resource
  private CommonExecuteService commonExecuteService;

  @Override
  public void executeSQL(String projectId, List<KeyAndValueDTO<Token, String>> sqlList,
      Boolean useDynamicDB) {
    //循环处理SQL
    for (KeyAndValueDTO<Token, String> kav : sqlList) {
      String execSqlText = kav.getValue();
      if (execSqlText.endsWith(";")) {
        execSqlText = execSqlText.substring(0, execSqlText.length() - 1);
      }
      //如果不适用动态数据源则增加一个找不到的项目id
      String databaseId = projectId;
      if (!useDynamicDB) {
        databaseId = projectId.concat(String.valueOf(System.currentTimeMillis()));
      }
      if (Token.SELECT == kav.getKey()) {
        //do nothing
        //执行存储过程
      } else if (Token.DECLARE == kav.getKey()) {
        commonExecuteService.callByString(databaseId, execSqlText);
        //其他都当修改执行
      } else {
        HashMap<String, Object> params = new HashMap<>(4);
        params.put(PROJECT_ID, projectId);
        commonExecuteService.updateByMap(databaseId, execSqlText, params);
      }
    }
  }


  /**
   * 根据操作对象获取操作类型
   *
   * @param stmt 操作对象
   * @param sql 数据库预计
   * @return 操作类型
   */
  private Token getOperation(SQLStatement stmt, String sql) {
    Token token;
    if (stmt instanceof SQLExprStatement) {
      throw new BusinessException(ResultCodeEnum.CODE4015, "sql语句非法,不支持:" + sql);
    } else if (stmt instanceof SQLDropUserStatement) {
      throw new BusinessException(ResultCodeEnum.CODE4015, "语句非法不能删除用户" + sql);
    } else if (stmt instanceof SQLDeleteStatement) {
      token = Token.DELETE;
    } else if (stmt instanceof SQLInsertStatement) {
      token = Token.INSERT;
    } else if (stmt instanceof SQLSelectStatement) {
      throw new BusinessException(ResultCodeEnum.CODE4015, "初始化不能执行select语句" + sql);
    } else if (stmt instanceof SQLCreateStatement) {
      token = Token.CREATE;
    } else if (stmt instanceof SQLDropStatement) {
      token = Token.DROP;
    } else if (stmt instanceof SQLAlterStatement) {
      token = Token.ALTER;
    } else if (stmt instanceof SQLCallStatement) {
      token = Token.DECLARE;
    } else {
      token = Token.UPDATE;
    }
    return token;
  }

}
